<!--
 Copyright 2015 the original author or authors.

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-->

<!doctype html>

<html class="no-js" lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>ConceptMap</title>
  <link rel="stylesheet" href="css/foundation.css"/>
  <link rel="stylesheet" href="css/vis.min.css"/>
  <link rel="stylesheet" href="css/concept-map.css"/>
  <script src="js/vendor/modernizr.js"></script>


</head>

<body>

<!-- Top bar -->

  <nav class="top-bar" data-topbar role="navigation">
    <ul class="title-area">
      <li class="name">
        <h1><a href="#">ConceptMap</a></h1>
      </li>
    </ul>

    <section class="top-bar-section">
      <ul class="right">
        <li><a href="#" onclick="openWikidataItem()" style="padding-left: 0px"><img src="img/wikidata-logo.png" ></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://wikidata.org" target="_blank"><i>Wikidata</i></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://graphenedb.com" target="_blank" style="padding: 0px"><img src="img/grapheneDB_logo_square.png" style="padding: 0px"></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://graphenedb.com" target="_blank"><i>GrapheneDB</i></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://neo4j.com" target="_blank" style="padding: 0px"><img src="img/neo4j-logo-square.png" style="padding: 0px"></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://neo4j.com" target="_blank"><i>Neo4j</i></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://pivotal.io/platform" target="_blank" style="padding: 0px"><img src="img/cloudfoundry-logo.png" style="padding: 0px"></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://pivotal.io/platform" target="_blank"><i>Cloud Foundry</i></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://projects.spring.io/spring-boot" target="_blank" style="padding: 0px"><img src="img/spring-logo.png" style="padding: 0px"></a></li>
      </ul>
      <ul class="right">
        <li><a href="http://projects.spring.io/spring-boot" target="_blank"><i>powered by Spring</i></a></li>
      </ul>
    </section>
  </nav>

  <div class="off-canvas-wrap" data-offcanvas>
    <div class="inner-wrap">
      <nav class="tab-bar">

        <section class="left-small">
          <a class="left-off-canvas-toggle menu-icon" href="#" onclick="retrieveLangLinks(curTitle, curLang)"><span></span></a>
        </section>

        <section class="middle tab-bar-section">
          <h1 id="offCanvasTitle" class="title">ConceptMap</h1>
        </section>

      </nav>

      <div class="row middle">
        <div class="small-12 large-12 columns">
          <input id="searchInput" type="text" placeholder="Search" style="margin-top: 10px;margin-bottom: 5px"
                 onKeyUp="searchArticleNameNearMatch()"/>
        </div>
      </div>

      <div class="row">
        <div class="small-2 small-offset-0 columns">
          <button id="searchDropdown" type="button" class="tiny" disabled="true" data-dropdown="searchDrop" style="padding: 0px; margin-top: 10px; margin-left: 0px; margin-right: 0px;" onclick="searchPopulateArticleNameMatches()" title="List of matching articles"><img src="img/dropdown-arrow-icon.png" width="44"/></button>
        </div>

        <div class="small-2 small-offset-0 columns">
          <button id="searchButton" type="button" class="tiny" disabled="true" style="padding: 0px;margin-top: 10px; margin-left: 0px; margin-right: 0px;" onclick="loadWikipediaPageByArticleName(curNearMatch, curLang)" title="Search for article"><img src="img/magnifying-glass.png" width="44"/></button>
        </div>

        <div class="small-2 small-offset-0 columns">
          <button id="clearMapButton" type="button" class="tiny" disabled="false" style="padding: 0px;margin-top: 10px; margin-left: 0px; margin-right: 0px;" onclick="clearConceptMap()" title="Erase concept map"><img src="img/erase.png" width="44"/></button>
        </div>

        <div class="small-2 small-offset-0 columns">
          <input id="pinSwitch" name="pinSwitchName" type="checkbox" onclick="handlePinSwitchClick()" checked
                 style="padding: 0px;margin-top: 20px; margin-left: 0px; margin-right: 0px; margin-bottom: 0px;">
          <label for="pinSwitch" >Pin item</label> <!--TODO: Internationalize this label -->
        </div>

        <div class="small-2 small-offset-0 columns">
          <button id="shareLinkButton" type="button" class="tiny" disabled="false" style="padding: 0px;margin-top: 10px; margin-left: 0px; margin-right: 0px;" onclick="revealShareLinkDialog()" title="Create link for this concept map"><img src="img/link.png" width="44"/></button>
        </div>

        <div class="small-2 small-offset-0 columns">
          <button id="helpAboutButton" type="button" class="tiny" disabled="false" style="padding: 0px;margin-top: 10px; margin-left: 0px; margin-right: 0px;" onclick="revealAboutDialog()" title="Helpful information"><img src="img/question-mark.png" width="44"/></button>
        </div>

        <ul id="searchDrop" class="f-dropdown" data-options="align:left" data-dropdown-content aria-hidden="true" tabindex="-1">
          <!-- This list is dynamically populated -->
        </ul>

      </div>

      <aside class="left-off-canvas-menu">
        <ul id="offCanvasLangLinks" class="off-canvas-list">
          <!-- This list is dynamically populated -->
        </ul>
      </aside>

      <a class="exit-off-canvas"></a>

      <section class="main-section">
        <div class="small-6 columns" style="margin-top: 0px;">
          <!--b>Concept Map:</b-->
          <div id="conceptMap"></div>
        </div>
        <div class="row">
          <div class="small-2 columns" style="margin-top: 10px;">
            <ul id="offCanvasList" class="side-nav panel"  style="padding-left:10px">
            </ul>
            <ul id="fromRelatedItemsList" class="side-nav panel"  style="padding-left:10px">
            </ul>
          </div>
          <div class="small-4 columns">
            <iframe id="wikipediaInnerFrame" src="/wikipage?name=Universe&lang=en" frameborder="0" scrolling="no" width="300"
                    onload="this.style.height=this.contentDocument.body.scrollHeight +'px'; refreshClaimsWikipageEndpoint(this.contentWindow.location.href)"></iframe>
          </div>
        </div>
      </section>
    </div>
  </div>

  <div id="shareLinkDialog" class="reveal-modal large" data-reveal aria-labelledby="sharelinkDialogTitle" aria-hidden="true" role="dialog">
    <div align="center">
      <h3 id="sharelinkDialogTitle">Share ConceptMap Hyperlink</h3>
      <p>Select, copy and share this hyperlink to your concept map</p>
      <p id="shareLink"></p>
      <a class="close-reveal-modal" aria-label="Close">&#215;</a>
    </div>
  </div>

<div id="noTraversalPathDialog" class="reveal-modal large" data-reveal aria-labelledby="noTraversalPathDialogTitle" aria-hidden="true" role="dialog">
  <div align="center">
    <h3 id="noTraversalPathDialogTitle">No Traversal Path</h3>
    <p>No traversal path found with specified conditions between the nodes</p>
    <p id="noTraversalPath"></p>
    <a class="close-reveal-modal" aria-label="Close">&#215;</a>
  </div>
</div>

<div id="aboutDialog" class="reveal-modal large" data-reveal aria-labelledby="aboutDialogTitle" aria-hidden="true" role="dialog">
    <div align="center">
      <h3 id="aboutDialogTitle">About ConceptMap</h3>
      <img src="img/conceptmap-icon-360x360.png" width="180" height="180">
      <p>Developed by <a href="https://twitter.com/javafxpert"
                         target="_blank">James L. Weaver</a></p>
      <p><a href="https://slides.com/javafxpert/conceptmap-technical" target="_blank">Getting Started with ConceptMap.io (slides)</a></p>
      <p><a href="http://learnjavafx.typepad.com/weblog/conceptmap/" target="_blank">ConceptMap.io posts on JavaFXpert.com blog</a></p>
      <p>Version 1.2 - released 25-Apr-2016<br/>
         Licensed under the Apache License, Version 2.0</p>
      <a class="close-reveal-modal" aria-label="Close">&#215;</a>
    </div>
  </div>


  <script src="js/d3.min.js"></script>
  <script src="js/vis.min.js"></script>
  <script src="js/vendor/jquery.js"></script>
  <script src="js/foundation.min.js"></script>
  <script>
    $(document).foundation({
      offcanvas : {
        // Sets method in which offcanvas opens.
        // [ move | overlap_single | overlap ]
        open_method: 'overlap_single',
        // Should the menu close when a menu link is clicked?
        // [ true | false ]
        close_on_click : false
      }
    });

  </script>

  <script>
    var curLang = "en";
    var curTitle = "";
    var curItemId = "Q1";
    var curPageItemId = "Q1";  //Id of page currently being displayed
    var curNearMatch = "";
    var dispIds = true;
    var visibleGraphDirty = true;

    // These variables hold the item ID, property ID, and direction for the start node of a shortest path pending request
    var shortestPathStartItemId = "";
    var shortestPathStartPropId = "";
    var shortestPathTravDirection = "";

    // TODO: Examine usefulness of, and possibly refactor, functionality in this handler
    window.addEventListener('load', function() {
      $('#searchButton').prop('disabled', true);
      $('#clearMapButton').prop('disabled', false);
      $('#shareLinkButton').prop('disabled', true);
      $('#helpAboutButton').prop('disabled', false);
      //$('#offCanvasList').foundation('offcanvas', 'toggle', 'offcanvas-overlap-left');
      $( "#wikipediaInnerFrame" ).attr("width",(window.innerWidth * .37) - 40); //.attr("height",window.innerHeight - 250);
      $(document).foundation({
        offcanvas : {
          // Sets method in which offcanvas opens.
          // [ move | overlap_single | overlap ]
          open_method: 'move',
          // Should the menu close when a menu link is clicked?
          // [ true | false ]
          close_on_click : true
        }
      });
    });

    /**
     * Retrieves Wikipedia article names for a string in an input field. This method is called
     * with each keystroke in the input field
     */
    function searchArticleNameNearMatch () {
      var searchStr = $('#searchInput').val().trim();
      $('#searchDropdown').prop('disabled', true);
      $('#searchButton').prop('disabled', true);
      if (searchStr.length > 0) {
        $('#searchDropdown').prop('disabled', false);
        searchStr = searchStr.split(" ").join("%20");
        $.ajax({
              url: '/articlesearch',
              data: "title=" + searchStr + "&nearmatch=true&lang=" + curLang, //TODO: Enable user to select lanquage
              dataType: 'json',
            })
            .done(function (response) {
              for (i in response.titles) {
                var title = response.titles[i];
                if (title.toLowerCase() === $('#searchInput').val().trim().toLowerCase()) {
                  $('#searchButton').prop('disabled', false);
                  curNearMatch = title;
                }
              }
            })
            .error(function (err) {
              //alert("error: " + err);
            });
      }
    }

    /**
     /**
     * Retrieves Wikipedia article names for a string in an input field. This method is called
     * with each keystroke in the input field
     */
    function searchPopulateArticleNameMatches () {
      var searchStr = $('#searchInput').val().trim();
      if (searchStr.length > 0) {
        searchStr = searchStr.split(" ").join("%20");
        $.ajax({
              url: '/articlesearch',
              data: "title=" + searchStr + "&lang=" + curLang, //TODO: Enable user to select lanquage
              dataType: 'json',
            })
            .done(function (response) {
              $('#searchDrop').html('');
              for (i in response.titles) {
                var title = response.titles[i];
                var snippet = "<li><a href='#' onclick='loadWikipediaPageByArticleName(\"" + title + "\", \"" + curLang + "\")'>" + title + "</a></li>";
                $('#searchDrop').append(snippet);
              }
            })
            .error(function (err) {
              //alert("error: " + err);
            });
      }
    }

    /**
     * Parses the
     * @param wikipageEndpointWithParams
     *
     * Examples of input include:
     * http://localhost:8080/wikipage?name=Earth&lang=en
     * http://localhost:8080/wikipage?name=Earth
     * http://localhost:8080/wikipage?name=Terre&lang=fr
     */
    function refreshClaimsWikipageEndpoint (endpointUrl) {
      // Scroll the main page to the top
      window.scrollTo(0, 0);

      // Clear the search input text field
      // TODO: Perhaps find a better place to clear the search input text field & suggestions dropdown
      $("#searchInput").val("");
      $('#searchDrop').html('');
      $('#searchButton').prop('disabled', true);
      $('#searchDropdown').prop('disabled', true);

      var endpointUrlArray = endpointUrl.split(/&|\?/);
      var articleName = "";
      var language = "";
      for (idx in endpointUrlArray) {
        var paramValueArray = endpointUrlArray[idx].split("=");
        if (paramValueArray.length == 2 && paramValueArray[0].toLowerCase() === "name") {
          articleName = paramValueArray[1];
        }
        else if (paramValueArray.length == 2 && paramValueArray[0].toLowerCase() === "lang") {
          language = paramValueArray[1];
        }
      }
      articleName = trimAnchor(articleName);
      if (articleName != "") {
        refreshClaims(articleName, language);
      }
    }

    var lastArticleNameRefreshed = "";
    var lastLangRefreshed = "";

    function refreshClaims (name, lang) {
      if (name != lastArticleNameRefreshed || lang != lastLangRefreshed) {
        lastArticleNameRefreshed = name;
        lastLangRefreshed = lang;
        $.ajax({
              url: '/idlocator',
              data: "name=" + name + "&lang=" + lang + "",
              dataType: 'json',
            })
            .done(function (itemInfo) {
              curPageItemId = itemInfo.itemId;
              retrieveClaimsByItemId(itemInfo.itemId, lang);
            })
            .error(function (err) {
              //alert("error: " + err);
            });
      }
    }

    function retrieveClaimsByItemId (id, lang) {
      $.ajax({
            url: '/claims',
            data: "id=" + id + "&lang=" + lang + "",
            dataType: 'json',
          })
          .done(function(data) { // Variable data contains the data we get from serverside
            populateOffCanvasList(data);
            retrieveRelatedClaimsByItemId(id, lang)
          })
          .error(function(err) {
            //alert("error: " + err);
          });
    }

    function retrieveRelatedClaimsByItemId (id, lang) {
      $('#fromRelatedItemsList').html('<label>Loading related items ...</label>'); // Clear out off-canvas list for related items
      $.ajax({
            url: '/relatedclaims',
            data: "id=" + id + "&lang=" + lang + "",
            dataType: 'json',
          })
          .done(function(data) { // Variable data contains the data we get from serverside
            populateFromRelatedItemsList(data)
          })
          .error(function(err) {
            $('#fromRelatedItemsList').html('<label>Error loading related items</label>');
          });
    }

    /*
     * Ascertain shortest paths between a given item and the (Entity Q35120) item that contains only
     * subclass-of (P279), instance-of (P31), and part-of
     * @param id Wikidata item ID for start of paths
     */
    function retrieveShortestPathsToRoot (id) {
      $('#pinSwitch').prop('checked', true); // In case pin checkbox isn't already set

      $.ajax({
            url: '/visrootpaths',
            data: "id=" + id + "",
            dataType: 'json',
          })
          .done(function (results) { // Variable data contains the data we get from serverside
            var nodes = results.nodes;
            if (nodes.length > 0) {
              for (var idx in nodes) {
                var itemId = nodes[idx].itemId;
                addItemId(itemId);
              }
              loadConceptMap();
            }
            else {
              $('#noTraversalPathDialog').foundation('reveal', 'open');
            }
          })
          .error(function (err) {
            //alert("error: " + err);
          });
    }

    /*
     * Traverse breadth-first an item
     * @param id Wikidata item ID
     * @param prop Wikidata property ID
     * @param direction "f" for forward, "r" for reverse, "u" for undirected
     * @param depth number of levels to traverse (0 defaults to a reasonable maximum)
     * @param limit maximum number of items to return (0 defaults to a reasonable maximum)
     */
    function traverseItems (id, prop, direction, depth, limit) {
      $('#pinSwitch').prop('checked', true); // In case pin checkbox isn't already set

      // Scroll the main page to the top
      window.scrollTo(0, 0);

      $.ajax({
            url: '/traversal',
            data: "id=" + id + "&prop=" + prop + "&direction=" + direction + "&depth=" + depth + "&limit=" + limit + "",
            dataType: 'json',
          })
          .done(function(results) { // Variable data contains the data we get from serverside
            var items = results.item;
            for (var idx in items) {
              var itemId = items[idx].id;
              addItemId(itemId);
            }
            loadConceptMap();
          })
          .error(function(err) {
            //alert("error: " + err);
          });
    }

    /*
     * Ascertain shortest path between two items.
     * @param id Wikidata item ID for start of path
     * @param prop Wikidata property ID.  If empty string, then calculate all shortest paths to a maximum of two hops,
     *             ignoring all arguments except for id and target
     * @param direction "f" for forward, "r" for reverse, "u" for undirected
     * @param depth number of levels to traverse (0 defaults to a reasonable maximum)
     * @param target Wikidata property ID for end of path
     * @param limit maximum number of items to return (0 defaults to a reasonable maximum)
     */
    function shortestPathNavigation (id, prop, direction, depth, target, limit) {
      $('#pinSwitch').prop('checked', true); // In case pin checkbox isn't already set

      if (prop.length > 0) {
        $.ajax({
              url: '/traversal',
              data: "id=" + id + "&prop=" + prop + "&direction=" + direction + "&depth=" + depth + "&target=" + target + "&limit=" + limit + "",
              dataType: 'json',
            })
            .done(function (results) { // Variable data contains the data we get from serverside
              var items = results.item;
              if (items.length > 0) {
                for (var idx in items) {
                  var itemId = items[idx].id;
                  addItemId(itemId);
                }
                loadConceptMap();
              }
              else {
                $('#noTraversalPathDialog').foundation('reveal', 'open');
              }
            })
            .error(function (err) {
              //alert("error: " + err);
            });
      }
      else {
          $.ajax({
                url: '/visshortpaths',
                data: "id=" + id + "&target=" + target + "",
                dataType: 'json',
              })
              .done(function (results) { // Variable data contains the data we get from serverside
                var nodes = results.nodes;
                if (nodes.length > 0) {
                  for (var idx in nodes) {
                    var itemId = nodes[idx].itemId;
                    addItemId(itemId);
                  }
                  loadConceptMap();
                }
                else {
                  $('#noTraversalPathDialog').foundation('reveal', 'open');
                }
              })
              .error(function (err) {
                //alert("error: " + err);
              });

      }
    }

    /*
     * Note the most recent item ID, property ID, and direction for the start node of a shortest path request
     */
    function noteShortestPathStartInfo(itemId, propId, travDirection) {
      //$('#pinSwitch').prop('checked', true); // In case pin checkbox isn't already set

      // Scroll the main page to the top
      window.scrollTo(0, 0);

      shortestPathStartItemId = itemId;
      shortestPathStartPropId = propId;
      shortestPathTravDirection = travDirection;
    }

    // Supply id and lang
    function loadWikipediaPageByItemId(id, lang) {
      $.ajax({
            url: '/locator',
            data: "id=" + id + "&lang=" + lang + "",
            dataType: 'json',
          })
          .done(function(itemInfo) {
            var artName = itemInfo.articleName;
            if (artName !== null) {
              if (id === curPageItemId) {
                // If the page hasn't changed, force the claims info to be refreshed, because it won't be performed
                // as a result of the page in the iFrame being replaced.
                retrieveClaimsByItemId(id, lang);
              }
              curPageItemId = id;
              $("#wikipediaInnerFrame").attr("src", "/wikipage?name=" + itemInfo.articleName + "&lang=" + lang);
            }
            else {
              retrieveClaimsByItemId(id, lang);
            }
          })
          .error(function(err) {
            //alert("error: " + err);
          });
    }

    function loadWikipediaPageByArticleName(name, lang) {
      curLang = lang;
      $("#wikipediaInnerFrame").attr("src", "/wikipage?name=" + name.trim() + "&lang=" + lang);
    }

    var chosenPropId = "";
    function popOffCanvasList(propArg) {
      if (propArg === chosenPropId) {
        chosenPropId = "";
      }
      else {
        chosenPropId = propArg;
      }
      populateOffCanvasList(null);
    }

    var lastResp = null;
    function populateOffCanvasList(respArg) {
      var resp = null;
      if (respArg == null) {
        resp = lastResp;
      }
      else {
        lastResp = respArg;
        resp = respArg;
      }
      $('#offCanvasList').html(''); // Clear out off-canvas list
      //$('#fromRelatedItemsList').html('<label>Loading related items ...</label>'); // Clear out off-canvas list for related items

      // Create button that enables user to find the items in common between two item IDs that aren't already directly related.  Note that an empty property is passed
      // into noteShortestPathStartInfo() to indicate that any paths may consist of any relationships.  Paths computed will be maximum of two hops.
      $('#offCanvasList').append("<input type='button' value='ic' onclick='noteShortestPathStartInfo(curItemId,\"\", \"u\")' title='Find all items directly in common'></input>");
      $('#offCanvasList').append("<input type='button' value='rp' onclick='retrieveShortestPathsToRoot(curItemId)' title='Find shortest paths to root (Entity)'></input>");

      $('#offCanvasList').append("&nbsp;" + "<b>Wikidata Relationships:</b><br/><br/>"); //TODO: Internationalize this label
      var title = resp.articleTitle;
      curTitle = title;
      var language = resp.lang;
      var itemId = ""
      var isProperty;

      if (title == null) title = "Wikipedia article missing";
      curItemId = resp.articleId;

      // Update concept map pin switch
      if (isItemIdInConceptMap(curItemId)) {
        $('#pinSwitch').prop('checked', true);
      }
      else {
        $('#pinSwitch').prop('checked', false);
      }

      $('#offCanvasTitle').text(title + (dispIds ? (" :" + curItemId) : "") + " [" + language + "]");

      for (var cIdx in resp.claim) {
        var propId = resp.claim[cIdx].prop.id;
        if (propId.length > 1 && propId.substring(0, 1).toUpperCase() === "P") {
          if (resp.claim[cIdx].val.length == 1) {
            $('#offCanvasList').append("<li>" +
                "<input type='button' value='x1' onclick='traverseItems(curItemId,\"" + propId + "\", \"f\", 1, 0)' title='Expand items to one level'></input>" + "&nbsp;" +
                "<input type='button' value='x*' onclick='traverseItems(curItemId,\"" + propId + "\", \"f\", 0, 0)' title='Expand items to multiple levels'></input>" + "&nbsp;" +
                "<input type='button' value='pu' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"u\")' title='Find shortest path undirected'></input>" + "&nbsp;" +
                "<input type='button' value='pf' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"f\")' title='Find shortest path in forward direction'></input>" +
                "<label onclick='popOffCanvasList(\"" + propId + "\")'><b>" +
                ((propId === chosenPropId) ? ("- ") : ("+ ")) + "</b>" +
                resp.claim[cIdx].prop.label + (dispIds ? (" :" + propId) : "+") + "</label></li>");

            if (propId === chosenPropId) {
              itemId = resp.claim[cIdx].val[0].id;
              isProperty = itemId.substring(0, 1).toUpperCase() === "P";
              $('#offCanvasList').append("<li><a href='#' onclick='loadWikipediaPageByItemId(\"" + itemId + "\", \"" + language + "\")'>" +
                  resp.claim[cIdx].val[0].label + (dispIds ? (" :" + itemId) : "") +
                  (dispIds ? ("") : "") +
                  "</a></li>");
            }

          }
          else {
            $('#offCanvasList').append("<li>" +
                "<input type='button' value='x1' onclick='traverseItems(curItemId,\"" + propId + "\", \"f\", 1, 0)' title='Expand items to one level'></input>" + "&nbsp;" +
                "<input type='button' value='x*' onclick='traverseItems(curItemId,\"" + propId + "\", \"f\", 0, 0)' title='Expand items to multiple levels'></input>" + "&nbsp;" +
                "<input type='button' value='pu' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"u\")' title='Find shortest path undirected'></input>" + "&nbsp;" +
                "<input type='button' value='pf' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"f\")' title='Find shortest path in forward direction'></input>" +
                "<label onclick='popOffCanvasList(\"" + propId + "\")'><b>" +
                ((propId === chosenPropId) ? ("- ") : ("+ ")) + "</b>" +
                resp.claim[cIdx].prop.label + (dispIds ? (" :" + propId) : "+") + "</label></li>");

            if (propId === chosenPropId) {
              var vals = resp.claim[cIdx].val;
              for (var vIdx in vals) {
                itemId = vals[vIdx].id;
                isProperty = itemId.substring(0, 1).toUpperCase() === "P";
                $('#offCanvasList').append("  <li><a href='#' onclick='loadWikipediaPageByItemId(\"" + itemId + "\", \"" + language + "\")'>" +
                    vals[vIdx].label + (dispIds ? (" :" + itemId) : "") +
                    (dispIds ? ("") : "") +
                    "</a></li>");
              }
            }

            $('#offCanvasList').append("  </ul>");
            $('#offCanvasList').append("</li>");
          }
        }
      }
      $('#offCanvasList').append("</ul>");
    }

    var chosenRelatedPropId = "";
    function popFromRelatedItemsList(propArg) {
      if (propArg === chosenRelatedPropId) {
        chosenRelatedPropId = "";
      }
      else {
        chosenRelatedPropId = propArg;
      }
      populateFromRelatedItemsList(null);
    }

    var lastRelatedResp = null;
    function populateFromRelatedItemsList(respArg) {
      var resp = null;
      if (respArg == null) {
        resp = lastRelatedResp;
      }
      else {
        lastRelatedResp = respArg;
        resp = respArg;
      }
      $('#fromRelatedItemsList').html(''); // Clear out off-canvas list
      $('#fromRelatedItemsList').append("<b><i>From Related Items:</i></b><br/><br/>"); //TODO: Internationalize this label

      var language = resp.lang;
      var itemId = ""
      var isProperty;

      for (var cIdx in resp.claim) {
        var propId = resp.claim[cIdx].prop.id;
        if (resp.claim[cIdx].val.length == 1) {
          $('#fromRelatedItemsList').append("<li>" +
              "<input type='button' value='x1' onclick='traverseItems(curItemId,\"" + propId + "\", \"r\", 1, 0)' title='Expand items to one level'></input>" + "&nbsp;" +
              "<input type='button' value='x*' onclick='traverseItems(curItemId,\"" + propId + "\", \"r\", 0, 0)' title='Expand items to multiple levels'></input>" + "&nbsp;" +
              "<input type='button' value='pu' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"u\")' title='Find shortest path undirected'></input>" + "&nbsp;" +
              "<input type='button' value='pr' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"r\")' title='Find shortest path in reverse direction'></input>" +
              "<i><label onclick='popFromRelatedItemsList(\"" + propId + "\")'><b>" +
              ((propId === chosenRelatedPropId) ? ("- ") : ("+ ")) + "</b>" +
              resp.claim[cIdx].prop.label + (dispIds ? (" :" + propId) : "+") + "</label></i></li>");

          if (propId === chosenRelatedPropId) {
            itemId = resp.claim[cIdx].val[0].id;
            isProperty = itemId.substring(0, 1).toUpperCase() === "P";
            $('#fromRelatedItemsList').append("<li><a href='#' onclick='loadWikipediaPageByItemId(\"" + itemId + "\", \"" + language + "\")'><i>" +
                resp.claim[cIdx].val[0].label + "</i>" + (dispIds ? (" <i>:" + itemId) : "") +
                (dispIds ? ("</i>") : "") +
                "</a></li>");
          }
        }
        else {
          $('#fromRelatedItemsList').append("<li>" +
              "<input type='button' value='x1' onclick='traverseItems(curItemId,\"" + propId + "\", \"r\", 1, 0)' title='Expand items to one level'></input>" + "&nbsp;" +
              "<input type='button' value='x*' onclick='traverseItems(curItemId,\"" + propId + "\", \"r\", 0, 0)' title='Expand items to multiple levels'></input>" + "&nbsp;" +
              "<input type='button' value='pu' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"u\")' title='Find shortest path undirected'></input>" + "&nbsp;" +
              "<input type='button' value='pr' onclick='noteShortestPathStartInfo(curItemId,\"" + propId + "\", \"r\")' title='Find shortest path in reverse direction'></input>" +
              "<i><label onclick='popFromRelatedItemsList(\"" + propId + "\")'><b>" +
              ((propId === chosenRelatedPropId) ? ("- ") : ("+ ")) + "</b>" +
              resp.claim[cIdx].prop.label + (dispIds ? (" :" + propId) : "+") + "</label></i></li>");

          if (propId === chosenRelatedPropId) {
            var vals = resp.claim[cIdx].val;
            for (var vIdx in vals) {
              itemId = vals[vIdx].id;
              isProperty = itemId.substring(0, 1).toUpperCase() === "P";
              $('#fromRelatedItemsList').append("  <li><a href='#' onclick='loadWikipediaPageByItemId(\"" + itemId + "\", \"" + language + "\")'><i>" +
                  vals[vIdx].label + "</i>" + (dispIds ? (" <i>:" + itemId) : "") +
                  (dispIds ? ("</i>") : "") +
                  "</a></li>");
            }
          }

          $('#fromRelatedItemsList').append("  </ul>");
          $('#fromRelatedItemsList').append("</li>");
        }
      }
      $('#fromRelatedItemsList').append("</ul>");

      loadConceptMap();
    }

    function retrieveLangLinks (title, lang) {
      //alert("Retrieving language links for: " + title + ", " + lang);
      var titleStr = title.split(" ").join("%20");
      $.ajax({
            url: '/langlinks',
            data: "title=" + titleStr + "&lang=" + lang + "",
            dataType: 'json',
          })
          .done(function(resp) {
            populateLangLinks(resp)
          })
          .error(function(err) {
            //alert("error: " + err);
          });
    }

    function populateLangLinks(resp) {
      if (resp.langlinks.length > 0) {
        $('#offCanvasLangLinks').html(''); // Clear out language links
        $('#offCanvasLangLinks').append("<ul><li><label>Languages:</label></li>"); //TODO: Internationalize this label
        for (var idx in resp.langlinks) {
          var langLink = resp.langlinks[idx];
          var langName = langLink.langName;
          var langAutonym = langLink.langAutonym;
          var langCode = langLink.langCode;
          var articleName = langLink.articleName;
          var articleTitle = langLink.articleTitle;

          articleTitle = trimAnchor(articleTitle);

          // TODO: Experiment more with this approach:
          $('#offCanvasLangLinks').append("<li><a href='#' onclick='touchClaimsInItemIdArray(\"" + langCode + "\"); visibleGraphDirty = true; loadWikipediaPageByArticleName(\"" + articleName + "\", \"" + langCode + "\")'>" +
              langName + " / " + langAutonym + "</a></li>");
        }
        $('#offCanvasLangLinks').append("</ul>");
      }
    }

    function revealShareLinkDialog() {
      $.ajax({
            url: '/bitly',
            data: "items=" + exportItemIds() + "&lang=" + curLang,
            dataType: 'json',
          })
          .done(function(bitly) {
            var statCode = bitly.status_code;
            if (statCode == 200) {
              $('#shareLink').text(bitly.url);
            }
            else {
              $('#shareLink').text("Link not retrieved, status_code: " + bitly.status_code);
            }
          })
          .error(function(err) {
            $('#shareLink').text("Error retrieving link");
            //alert("error: " + err);
          });
      $('#shareLinkDialog').foundation('reveal', 'open');
    }

    function revealAboutDialog() {
      $('#aboutDialog').foundation('reveal', 'open');
    }

    function openWikidataItem() {
      window.open('https://www.wikidata.org/wiki/' + curItemId);
    }

    /**
     * Trims the anchor, if present, from an article name
     */
    function trimAnchor(articleStr) {
      var retStr = articleStr;
      if (retStr != null && retStr.length > 0) {
        var hashPos = retStr.indexOf("#");
        if (hashPos >= 0) {
          retStr = retStr.substring(0, hashPos);
        }
      }
      return retStr;
    }

    // Throttled resize function
    $(window).on('resize', Foundation.utils.throttle(function(e){
      $( "#wikipediaInnerFrame" ).attr("width",(window.innerWidth * .37) - 40); //.attr("height",window.innerHeight - 250);
    }, 300));

    $("input").keypress(function(event){
      var keycode = (event.keyCode ? event.keyCode : event.which);
      if(keycode == '13'){
        if (curNearMatch.length > 0) {  //TODO: Also check if Search button is enabled (or better yet, a common "is" method?
          loadWikipediaPageByArticleName(curNearMatch, curLang);
        }
      }
      else {
        searchArticleNameNearMatch();
      }
    });
  </script>

  <!-- Concept Map related functionality -->
  <script>
    var w = window,
        d = document,
        e = d.documentElement,
        g = d.getElementsByTagName('body')[0],
        x = w.innerWidth || e.clientWidth || g.clientWidth,
        y = w.innerHeight|| e.clientHeight|| g.clientHeight;

    var width = x * .50,
        height = (y - 270) * 1.2;

    /*
     * Directed functionality implemented with visjs
     */
    function loadConceptMap() {
      var itemsStr = itemIdArray.join(",");
      if (!visibleGraphDirty) {
        return;
      }
      else {
        visibleGraphDirty = false;
      }
      $( "#conceptMap" ).empty();
      $( "#conceptMap" ).css("width", width);
      $( "#conceptMap" ).css("height", height);

      //var network;
      //var nodes;
      //var edges;

      $.ajax({
            url: 'http://conceptmap.cfapps.io/visgraph',
            //url: 'http://localhost:8080/visgraph',
            data: "items=" + itemsStr,
            dataType: 'json'
          })
          .done(function(results) {
            var nodes = new vis.DataSet(results.nodes);
            var edges = new vis.DataSet(results.edges);

            // create a network
            var container = document.getElementById('conceptMap');
            var data = {
              nodes: nodes,
              edges: edges
            };
            var options = {
              nodes: {
                borderWidth:1,
                size: 30,
                color: {
                  border: '#406897',
                  background: '#6AAFFF'
                },
                font:{color:'#000000'},
                shape: 'circularImage',
                shapeProperties: {
                  useBorderWithImage:true
                }
              },
              edges: {
                color: 'lightgray',
                font: {color:'#000000', size: 8, align: 'top'}
              }
            };
            network = new vis.Network(container, data, options);

            network.on("click", function(args) {
              if (args.nodes.length > 0) {
                var itemId = "Q" + args.nodes[0];

                if (shortestPathStartItemId.length > 0) {
                  // There is a pending request to do a shortest path navigation
                  shortestPathNavigation (shortestPathStartItemId, shortestPathStartPropId, shortestPathTravDirection, 0, itemId ,0);
                  shortestPathStartItemId = "";
                  shortestPathStartPropId = "";
                  shortestPathTravDirection = "";
                }
                else {
                  loadWikipediaPageByItemId(itemId, curLang);
                }
              }
            });

          })

          .error(function(err) {
            //alert("error: " + err);
          });


    }

    loadWikipediaPageByItemId("Q1", curLang);

    // Contains item IDs to display in concept map
    var itemIdArray = ["Q1"];

    // Support a language specified as an argument, but need to handle multiple languages in the Neo4j database befor publicizing
    var langArgStr = getArgumentByName("lang");
    if (langArgStr.length > 0) {
      curLang = langArgStr;
    }

    var itemsArgStr = getArgumentByName("items");
    if (itemsArgStr.length > 0) {
      importItemIds(itemsArgStr);
    }

    // Cause each ID in array to be merged into neo4j database
    touchClaimsInItemIdArray(curLang);


    /*
     * Function that retrieves an argument from the URL (self.location.search)
     */
    function getArgumentByName(name) {
      name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
      var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
          results = regex.exec(self.location.search);
      return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    }

    /*
     * Scrub a String containing comma-separated item IDs and populate itemIdArray
     */
    function importItemIds (itemIds) {
      itemIdArray=[];
      var tempArray = itemIds.split(",");
      for (var i = 0; i < tempArray.length; i++) {
        var itemStr = tempArray[i];
        itemStr = itemStr.trim().toUpperCase();
        if (itemStr.length > 0 && itemStr.substring(0, 1) === "Q") {
          itemIdArray.push(itemStr);
        }
      }
      $('#shareLinkButton').prop('disabled', itemIdArray.length == 0);

      // Set the current item ID to the first ID in the supplied list of items
      if (itemIdArray.length > 0) {
        curItemId = itemIdArray[0];
        loadWikipediaPageByItemId(curItemId, curLang);
      }
    }

    /**
     * Return comma-separated item ID string from itemIdArray.
     * Set the first element of the array to the current item ID so that the
     * corresponding Wikipedia article will be displayed when loaded
     */
    function exportItemIds() {
      if (curItemId.length > 0 && curItemId.substring(0, 1).toUpperCase() == "Q") {
        var indexOfCurItemId = itemIdArray.indexOf(curItemId);
        if (indexOfCurItemId > 0) {
          var tempItemId = itemIdArray[0];
          itemIdArray[0] = curItemId;
          itemIdArray[indexOfCurItemId] = tempItemId;
        }
      }
      return itemIdArray.join(",");
    }

    function addItemId(itemId) {
      if (itemId.length > 0) {
        if (itemIdArray.indexOf(itemId) < 0) {
          itemIdArray.push(itemId);
        }
      }
      $('#shareLinkButton').prop('disabled', itemIdArray.length == 0);
      visibleGraphDirty = true;
    }

    function removeItemId(itemId) {
      if (itemId.length > 0) {
        var index = itemIdArray.indexOf(itemId);
        if (index > -1) {
          itemIdArray.splice(index, 1);
        }
      }
      $('#shareLinkButton').prop('disabled', itemIdArray.length == 0);
      visibleGraphDirty = true;
    }

    function isItemIdInConceptMap(itemId) {
      return itemIdArray.indexOf(itemId) > -1;
    }

    /**
     * Cause the neo4j database to be loaded with item info
     * @param id
     * @param lang
     */
    function touchClaimsInItemIdArray (lang) {
      for (var i = 0; i < itemIdArray.length; i++) {
        var id = itemIdArray[i];
        $.ajax({
              url: '/claims',
              data: "id=" + id + "&lang=" + lang + "",
              dataType: 'json',
            })
            .done(function (data) { // Variable data contains the data we get from serverside
              //
            })
            .error(function (err) {
              //alert("error: " + err);
            });
      }
    }

    function handlePinSwitchClick() {
      var isOn = $('#pinSwitch').is(':checked');
      if (isOn) {
        addItemId(curItemId);
      }
      else {
        removeItemId(curItemId);
      }
      loadConceptMap();
    }

    function clearConceptMap(itemId) {
      itemIdArray = [];
      $('#pinSwitch').prop('checked', false);
      visibleGraphDirty = true;
      loadConceptMap();
    }

    /**
     * TODO: Populate this function
     */
    function generateExportLink() {

    }

  </script>

</body>
</html>
